import com.mosesSupposes.fuse.FuseKitCommon;
import com.mosesSupposes.fuse.ZManager;

/**
* The Fuse Kit [build1.1z3]
* Copyright (c) 2006 Moses Gunesch, MosesSupposes.com
* 
* Distributed under MIT Open Source License, see Fuse-Kit-License.html (in fuse package directory)
* Easing Equations (c) 2003 Robert Penner used by permission, see PennerEasing
* Visit http://www.mosessupposes.com/Fuse
*
* @ignore
*
* A robust central processing engine for all scripted tweens in a swf published to AS2.<br>
* <br>
* @usage
* Your project can be prepared to use ZigoEngine in these ways:
* <ul><li>Simply import this class and begin using static methods like {@link #doTween}.<br>
*  - You may then optionally extend the engine's capabilities using {@link #register}.<br></li>
* <li>Or, use {@link #simpleSetup} to add tweening shortcuts like <code>alphaTo</code> to MovieClips, Buttons and TextFields.</li></ul>
* <br>
* Extending prototypes is entirely optional in this version - all Shortcut functionality is excluded from the core engine class.<br><br>
* Events dispatched by ZigoEngine (see {@link #addListener} for more info):
* <ul><li><code>onTweenInterrupt</code></li></ul>
* Events dispatched by individual target objects (see {@link com.mosesSupposes.fuse.ZManager} for more info):
* <ul><li><code>onTweenStart</code></li>
* <li><code>onTweenUpdate</code></li>
* <li><code>onTweenEnd</code></li></ul>
* 
* @author	Moses Gunesch / MosesSupposes.com / ZigoEngine based on concepts by Ladislav Zigo, Zeh Fernando
* @version	2.0
*/
class com.mosesSupposes.fuse.ZigoEngine
{
	/**
	 * Enables kit version to be retrieved at runtime or when reviewing a decompiled swf. 
	 */
	public static var VERSION:String = FuseKitCommon.VERSION+', ZigoEngine based on concepts by Ladislav Zigo, laco.wz.cz/tween';
	
	/**
	 * Default easing if unspecified.
	 * @description	This default is hardcoded in the engine and doesn't require that {@link com.mosesSupposes.fuse.PennerEasing} is registered. 
	 * However you will need to register that class with the engine if you wish to set this property to a similar string like "easeOutQuad". 
	 * You may also set this property to any standard easing function (like <code>mx.transitions.easing</code>).
	 */
	public static var EASING:Object = 'easeOutQuint';
	
	/**
	 * Default tween duration if unspecified.
	 */
	public static var DURATION:Number = 1;
	
	/**
	 * If set true the engine always rounds its math (helpful with pixelfonts).
	 * @description	Degradation in animation and performance is minor with this feature enabled. Hence it was left a global toggle with the assumption that it should only be set true for projects requiring rounding.
	 */
	public static var ROUND_RESULTS:Boolean = false;
	
	/**
	* Controls how much feedback the engine outputs, helpful for debugging.
	* @usage
	* <ul>
	* 	<li>0 = no traces,</li> 
	*	<li>1 = normal errors & warnings</li>
	*	<li>2 = additional Fuse output</li>
	*	<li>3 = additional FuseItem output</li>
	* </ul>
	*/
	public static var OUTPUT_LEVEL:Number = 1;
	
	/**
	 * (Legacy) Option to stop all tweening props in target as a new tween is added.
	 */ 
	public static var AUTOSTOP:Boolean = false; 
	
	/**
	 * (Advanced) Sets default engine behavior for situations where tweens fail to affect a visible change or have no duration.
	 * @usage <b>1. No-duration tweens:</b><br>
	 * Tweens with a 0-second duration set the property immediately at the time of the tween call, or after any delay.<br>
	 * This behavior has advantages and disadvantages. It is <i>recommended</i> that you purposefully use 0-second tweens 
	 * to set properties that are handled by the engine at any time in your program, since this will 
	 * effectively stop any potentially running tweens on that property. (Trying to set a property while it is being tweened 
	 * usually has no effect.) However in some dynamic programs you may want tweens that end up having no duration to also
	 * skip their delay and/or callbacks and events - this can be controlled with SKIP_LEVEL.<br><br> 
	 * <b>2. No-change tweens:</b><br>
	 * Tweens at times end up being generated that affect no visual change. Normally the ZigoEngine operates under its 
	 * legacy behavior, which is to run such tweens regardless. This can be good (the program's sequence will continue 
	 * on a schedule) or bad (the tween is essentially invisible and will fire callbacks and events on update and end, 
	 * which may cause confusion). In some cases you may want to skip past tweens that don't do anything visually.<br>
	 * <br>
	 * The <code>SKIP_LEVEL</code> default, as well as the per-tween <code>skipLevel</code> parameter (see {@link #doTween}) 
	 * enable you to control the engine's behavior in these situations more accurately:
	 * <ul><li>0= Default.<br>No-change tweens are run with their full duration, delay, and callbacks/events. 
 	 * No-duration tweens set the property and execute all callbacks and events immediately or after a delay.</li>
	 * <li>1= Skip no-change tweens.<br>Tweens that affect no change are discarded (both delay and duration are ignored). Callbacks/events are fired immediately. No-duration tweens ignore delay and set properties immediately.</li>
	 * <li>2= Do not fire events or callbacks for no-change or no-duration tweens.<br>No-change tween calls are entirely ignored. No-duration tweens ignore delay and set properties immediately.</li></ul>
	 * <br>To review what property tweens were successfully added, {@link #doTween} returns a formatted list.
	 */
	public static var SKIP_LEVEL:Number = 0;
	
	/**
	 * A movieclip is created in the _root timeline to run the engine on an EnterFrame pulse.
	 * @description	Use {@link #setControllerDepth} to set the clip's level. By default it is set on depth 6789. A depth of 0 will be more convenient if you'll be using depth management in _root.
	 */ 
	private static var tweenHolder:MovieClip;
	
	/**
	 * Zmanager instance
	 * @description	Broken out to increase speed in as2
	 */
	private static var instance:ZManager;
	
	/**
	 * Internal setInterval id memory for ability to clearInterval
	 */
	private static var updateIntId:Number;
	
	/**
	 * Internal setting for running the engine on a setInterval pulse.
	 * @see	setUpdateInterval
	 */
	private static var updateTime:Number;
	
	/**
	 * Internal boolean playing switch
	 */
	private static var _playing:Boolean = false;
	
	/**
	 * Internal counter for hidden __zigoID__ parameter written into target objects
	 */
	private static var zigoIDs:Number = 0;
	
	/**
	 * Internal table for externally registered classes which are stored via their registryKey string.
	 */
	private static var extensions:Object;
	
	/**
	 * Internal counter used to tag each callback function with a unique id
	 */
	private static var cbTicker:Number = 0;
	
	/**
	* Do not use constructor. All methods of engine are static.
	*/
	private function ZigoEngine(){} 
	
	/**
	 * Written by AsBroadcaster
	 * @ignore
	 * (Advanced) Use this method to add a listener for "onTweenInterrupt" event, which is broadcast directly by the engine.
	 * @usage	The "onTweenInterrupt" event is fired in the following situations:
	 * <ul><li>A target goes missing, for instance if a MovieClip was removed during a tween.</li><li>Another tween call overwrote one or more property tweens in progress</li><li>A <code>removeTween</code> call stopped one or more tweens in progress.</li><li>A target is deinitialized from the engine during tweens</li></ul>
	 * This event is special in that it is dispatched directly by the ZigoEngine class, unlike target-events such as <code>onTweenEnd</code> (see: {@link com.mosesSupposes.fuse.ZManager} for more info). 
	 * This ensures the event is fired even when targets go missing and are thus unable to dispatch the event. 
	 * To aid in tracking targets in that situation, the internal <code>__zigoID__</code> property for each target is also passed in the event object. Any initialized/tweening target contains this read-only property, making it possible to advance-query then later identify targets after they're removed.
	 * <pre>var myListener:Object = {
	 * 	onTweenInterrupt:function(o:Object):Void {
	 * 		trace("Interruption detected:");
	 * 		trace(" -target (may not exist if removed):"+o.target);
	 * 		trace(" -target id:"+o.__zigoID__);
	 * 		trace(" -properties interrupted:"+(o.props).toString());
	 * 	}
	 * };
	 * ZigoEngine.addListener(myListener);
	 * ZigoEngine.doTween(my_mc,"_alpha",0,2);
	 * // click the mouse during the tween to receive the event.
	 * function onMouseDown():Void {
	 * 	my_mc.removeMovieClip();
	 * }
	 * </pre>
	 * @param the function or object that should be called
	 */
	public static function addListener(handler:Object):Void
	{
		AsBroadcaster.initialize(ZigoEngine);
		ZigoEngine.addListener(handler);
	}
	
	/**
	 * Written by AsBroadcaster
	 * @ignore
	 * Remove a listener for onTweenInterrupt event
	 * @param the function or object that should be called
	 */
	public static function removeListener(handler:Object):Void {}
	
	/**
	* Returns true if the engine contains tweens and is running updates on a pulse
	*/
	public static function isPlaying():Boolean
	{
		return _playing;
	}
	
	/**
	* An alternative setup command that extends prototypes with shortcuts like <code>alphaTo</code>.
	* @description	This setup command should only be called once at the beginning of your program. 
	* Use either this or {@link #register}, not both. 
	* The difference is that <code>simpleSetup</code> alters base prototypes so that you may call tween methods 
	* directly on targets, such as <code>my_mc.alphaTo(0);</code>.<br><br>Example:
	* <pre>import com.mosesSupposes.fuse.*;
	* ZigoEngine.simpleSetup(Shortcuts,PennerEasing);</pre>
	* Note that to keep filesize to a minimum you should only register the features you intend to use. Use publish settings to "omit trace actions" to further reduce filesize.
	* @param	shortcutsClass <b>It is madadory to pass the {@link #Shortcuts} class during this call!</b> You may additionally pass any of the following classes (all are optional), as in the way PennerEasing is added above.
	* <li><code>PennerEasing</code> Enables shortcut strings like "easeOutQuad" to be used with the engine. See {@link com.mosesSupposes.fuse.PennerEasing}</li>
	* <li><code>Fuse</code> Enables Fuse for animation sequencing. Note that if animation won't be sequenced you do may simply import and use Fuse without ZigoEngine. See {@link com.mosesSupposes.fuse.Fuse}</li>
	* <li><code>FuseFMP</code> Enables shortcut strings like <code>DropShadow_distance</code> to be tweened in the engine. See {@link com.mosesSupposes.fuse.FuseFMP}</li>
	* <li><code>FuseItem</code> Enables Fuse-style Object Syntax to be used with the <code>ZigoEngine.doTween</code>. Note that Fuse does not need to be registered to use this feature. See {@link com.mosesSupposes.fuse.FuseItem}</li></ul>
	*/ 
	public static function simpleSetup(shortcutsClass:Function):Void
	{
		if (arguments.length>0) register.apply(ZigoEngine,arguments);
		_global.ZigoEngine = ZigoEngine; // This allows you to skip using import statements in the timeline.
		if (extensions.fuse!=undefined) _global.Fuse = extensions.fuse;
		if (extensions.fuseFMP!=undefined) extensions.fuseFMP.simpleSetup(); // adds _global.FuseFMP reference
		initialize(MovieClip.prototype, Button.prototype, TextField.prototype);
		if (extensions.shortcuts==undefined) { // ignores OUTPUT_LEVEL since it's crucial
			FuseKitCommon.error('001');
		}
	}
	
	/**
	* An optional setup command that registers additional classes for use with the ZigoEngine.
	* @description	This setup command should only be called once at the beginning of your program. 
	* Use either this or {@link #simpleSetup}, not both. 
	* The difference is that <code>register</code> links additional classes to the engine without altering base prototypes.
	* <pre>import com.mosesSupposes.fuse.*;
	* ZigoEngine.register(PennerEasing, Fuse, FuseFMP);</pre>
	* Note that to keep filesize to a minimum you should only register the features you intend to use. Use publish settings to "omit trace actions" to further reduce filesize.
	* @param classReference		 Pass any of the following classes (all are optional): 
	* <ul><li><code>Shortcuts</code> Enables {@link #doShortcut}, "easyfunc" callback parsing, and the option to copy / remove shortcuts on any target or prototype manually using {@link #initialize}. See {@link com.mosesSupposes.fuse.Shortcuts}</li>
	* <li><code>PennerEasing</code> Enables shortcut strings like "easeOutQuad" to be used with the engine. See {@link com.mosesSupposes.fuse.PennerEasing}</li>
	* <li><code>Fuse</code> Enables Fuse for animation sequencing. Note that if animation won't be sequenced you do may simply import and use Fuse without ZigoEngine. See {@link com.mosesSupposes.fuse.Fuse}</li>
	* <li><code>FuseFMP</code> Enables shortcut strings like <code>DropShadow_distance</code> to be tweened in the engine. See {@link com.mosesSupposes.fuse.FuseFMP}</li>
	* <li><code>FuseItem</code> Enables Fuse-style Object Syntax to be used with the <code>ZigoEngine.doTween</code>. Note that Fuse does not need to be registered to use this feature. See {@link com.mosesSupposes.fuse.FuseItem}</li></ul>
	*/
	public static function register(classReference:Function):Void
	{
		if (extensions==undefined) extensions = {};
		var supported:String = '|fuse|fuseItem|fuseFMP|shortcuts|pennerEasing|';
		for (var i:String in arguments) {
			var key:String = (arguments[i]).registryKey;
			if (extensions[key]==undefined && supported.indexOf('|'+key+'|')>-1) {
				extensions[key] = (arguments[i]); //retain class
				if (key=='fuseFMP' || key=='shortcuts') Object(extensions[key]).initialize(); // not entirely necessary.
			}
		}
	}
	
	/**
	* (Advanced) Prepares targets for use with engine.
	* @description This method is never needed when extending prototypes with <code>simpleSetup</code>. 
	* It can be used to initialize specific targets to accept listeners prior to tweening - see {@link com.mosesSupposes.fuse.ZManager} for an example.
	* If the Shortcuts class was passed to {@link #register}, this method also copies all tweening shortcuts to targets.
	* 
	* @param	One or more targets to initialize.
	*/
	public static function initialize(target:Object):Void
	{
		if (arguments.length>0) {
			initializeTargets.apply(ZigoEngine, arguments);
			if (extensions.shortcuts!=undefined) {
				extensions.shortcuts.addShortcutsTo.apply(extensions.shortcuts, arguments);
			}
		}
	}
	
	/** 
	* (Advanced) Clears targets from being used with the engine.
	* @description	Removes AsBroadcaster functionality (and tweening shortcuts if the Shortcuts class was registered) 
	* from any object previously initialized using <code>initialize</code>.<br><br>
	* @param	One or more targets to initialize, or nothing/null to deinitialize base prototypes (for example to undo simpleSetup)
	* @see initialize
	*/
	public static function deinitialize(target:Object):Void
	{
		if (arguments.length==0 || target==null) {
			arguments.push(MovieClip.prototype, Button.prototype, TextField.prototype);
		}
		deinitializeTargets.apply(ZigoEngine, arguments);
		if (extensions.shortcuts!=undefined) { 
			extensions.shortcuts.removeShortcutsFrom.apply(extensions.shortcuts, arguments);
		}
	}
	
	/**
	* @return	Number set using <code>setUpdateInterval</code>, or <code>undefined</code> if unset. 
	* @see	#setUpdateInterval
	*/
	public static function getUpdateInterval():Number {
		return updateTime;
	}
	
	/**
	* When set, the engine will run on a setInterval pulse instead of a frame pulse.
	* @param time		Pulse duration in milliseconds, or null/undefined to return to an frame-based pulse.
	* @description		In most cases it is best to leave the engine in its default state since a frame-based pulse usually renders more smoothly. 
	* The engine always uses seconds for tween duration so using a longer interval will only reduce animation quality, not slow tweens down. 
	* This may be desirable in some cases, for instance to reduce CPU usage in banners.
	*/
	public static function setUpdateInterval(time:Number):Void {
		if (_playing) {
			setup(true);
			updateTime = time;
			setup();
		}else{
			updateTime = time;
		}
	}
	
	/**
	* Depth of the ZigoEnginePulse clip.
	* @return		numeric depth in _root
	*/
	public static function getControllerDepth():Number { 
		return tweenHolder.getDepth(); 
	}
	
	/**
	* Creates a clip in the _root timeline called "ZigoEnginePulse" or sets its depth
	* @param	 If null or nothing passed the beacon clip is placed at depth 6789, but a depth of 0 will be more convenient if you'll be using depth management in _root.
	*/
	public static function setControllerDepth(depth:Number):Void {
		if (depth==null || _global.isNaN(depth)==true) {
			depth = 6789;
		}
		if(Object(tweenHolder).proof!=null) {
			tweenHolder.swapDepths(depth);
		}
		else {
			tweenHolder = _root.createEmptyMovieClip("ZigoEnginePulse", depth);
		}
	}

	/**
	* An alternative to {@link #doTween} that accepts Shortcut syntax. <b>Requires registration of the Shortcuts class.</b>
	* @description	This idea thanks to Yotam Laufer. Example:
	* <pre>ZigoEngine.doShortcut(my_mc, 'scaleTo', 200, 1, 'easeOutQuad');</pre>
	* See {@link com.mosesSupposes.fuse.ZigoEngine} introduction for instructions on registering Shortcuts to enable this feature.
	* @param targets	 	Tween target object or array of target objects
	* @param methodName	 	The shorcut as a string, such as "alphaTo", followed by parameters that follow that shortcut's format.
	* @return				A comma-delimited string of props successfully added. When multipe targets were passed, a pipe-delimited series is returned to enable verification of individual properties per target.
	*/
	public static function doShortcut(targets:Object, methodName:String):String
	{
		if (extensions.shortcuts==undefined) {
			if (OUTPUT_LEVEL>0) FuseKitCommon.error('002');
			return null;
		}
		return (extensions.shortcuts.doShortcut.apply(extensions.shortcuts, arguments));
	}
	
	/**
	* Generates and immediately begins one or more property-tweens running on one or more targets.
	* @description	<pre>ZigoEngine.doTween(my_mc, '_x,_y', [100,150], 1, Strong.easeInOut);</pre>
	* {@link com.mosesSupposes.fuse.Fuse}-style Object Syntax may also be used if the {@link com.mosesSupposes.fuse.FuseItem} class is registered:
	* <pre>ZigoEngine.doTween({ target:my_mc, start_alpha:0, y:150, ease:Strong.easeInOut });</pre>
	* @param targets		Tween target object or array of target objects
	* @param props			String like <code>"_x"</code>, comma-delimited list of properties like <code>"_x,_y"</code>, or Array of properties like <code>["_x","_y"]</code>.
	* <br>The engine accepts any existing property in the target including custom variables, and the following special properties:
	*	<ul>
	*	<li><code>_scale</code> Sets _xscale and _yscale to the same value.</li>
	*	<li><code>_size</code> Sets _width and _height to the same value.</li>
	*	<li><code>_frame</code> Tweens a MovieClip's timeline to a frame using easing specified.</li>
	*	<li><code>_tint</code> Accepts 0x000000 and "#000000" formats, null (full reset), or object <code>{tint:Number/null/hexString, percent:Number/String}</code>.</li>
	*	<li><code>_tintPercent</code> Value range 0-100</li>
	*	<li><code>_brightness</code> Value range 0-100</li>
	*	<li><code>_brightOffset</code> Burn effect. -100=black, 0=normal, 100=white</li>
	*	<li><code>_invertColor</code> Value range 0-100</li>
	*	<li><code>_colorReset</code> Value range is current tint - 100. Acts like the opposite of tintPercent where 100 is a full reset.</li>
	*	<li><code>_contrast</code> 0=gray, 100=normal, 200=high-contrast, higher=posterized</li>
	*	<li><code>_colorTransform</code> Generic object in Flash7 transform format <code>{ra:,rb:,etc.}</code></li>
	*	<li><code>_bezier_</code> Generic object with some or all of the properties <code>{x:,y:,controlX:,controlY:}</code>. Relative (string) values are okay. 
	*	Note that only one control param is necessary to generate a curved motion path, simply omit other values.<br>
	*	Example - swoop up then down while sliding right:<br>
	*	<code>ZigoEngine.doTween(clip1, "_bezier_", {x:"100",controlY:"-25"});</code></li>
	*	</ul>
	*	When {@link com.mosesSupposes.fuse.FuseFMP} is registered, a large number of BitmapFilter properties are tweenable:
	*	<ul><li><code>Blur_blurX, etc.</code> Format follows the convention short-filtername+"_"+filter property.</li></ul>
	* @param endvals		tween end-value or Array of corresponding end-values
	* <br><b>Relative values:</b> Passing a string instead of a number like <code>"-10"</code> vs. <code.-10</code> will treat the endvalue as relative to the start position of the tween.
	* @param seconds		tween duration. If nothing is passed, defaults to <code>ZigoEngine.DURATION</code>.
	* @param ease			function, shortcut-string, or custom-easing-panel object. If nothing is passed, defaults to <code>ZigoEngine.EASING</code>.
	* @param delay			seconds to wait before performing the tween. Start callback and <code>onTweenStart</code> event are fired at end of delay. 
	* @param callback		function, string, or object
	* <br>1. Function
	* <ul><li>myfunc</li>
	* <li>function(){ ... }</li>
	* <li>Delegate.create(....) - adds scope</li>
	* </ul>
	* <code>ZigoEngine.doTween(clip1,"_x","100",1,"easeOutQuad",0, myCallback);</code><br><br>
	* 2. String (also known as easyfunc in Fuse) - Note that you need to register Shortcuts to use this feature.
	* <ul><li>"myscope.callback();"</li>
	* <li>"myscope.myscope.myfunc(someexpression,'somestring',{someparam:someval});" - Scope can be complex, 
	* plus various param formats including object and array are parsed.</li>
	* </ul>
	* <code>ZigoEngine.doTween(clip1,"_x","100",1,"easeOutQuad",0, "myCallback();");</code><br><br>
	* 3. Callback Object - a generic object with the parseable params:
	* <ul>
	* <li><code>func</code> Function or string name of function</li>
	* <li><code>scope</code> The <i>this</i> Object where the callback should execute</li>
	* <li><code>args</code> One argument or an array of arguments to pass to the callback</li>
	* <li><code>startfunc</code></li>
	* <li><code>startscope</code></li>
	* <li><code>startargs</code></li>
	* <li><code>updfunc</code></li>
	* <li><code>updscope</code></li>
	* <li><code>updargs</code></li>
	* </ul>
	* <code>ZigoEngine.doTween(clip1,"_x","100",1,"easeOutQuad",0, {scope:this, func:"myCallback"});</code>
	* <br><br>
	* <b>Additional engine parameters</b> - Include any of these in a callback object:
	* <ul>
	* <li><code>cycles</code> Tweens back and forth between start and end.<br>
	* 0 or "LOOP" = infinite cycles, default = 1, 2 or more to specify cycles.</li>
	* <li><code>skipLevel</code> 0,1, or 2.  See {@link #SKIP_LEVEL} for details.<br>
	* Including this paramter in a callback object overrides the engine default on a per-tween basis.</li>
	* <li><code>extra1</code> Elastic easing amplitude or Back easing overshoot</li>
	* <li><code>extra2</code> Elastic easing period</li>
	* <li><code>easyfunc</code> String like "myClip.doSomething(true);" as in option 1 above.</li>
	* </ul>
	* @return	A formatted string:<br>
	* The engine reports which properties have been added to its active tween list by returning a comma-delimited string of just the properties that were successfully added, such as <code>"_x"</code> or <code>"_x,_y"</code>. 
	* These may occasionally differ from what was tweened, such as <code>"_width,_height"</code> when <code>"_size"</code> was tweened.<br><br>
	* If multiple targets are passed to the engine, a pipe-delimited series of properties is returned in target order, which enables granular review of whether specific properties for each target.<br>
	* <pre>var results:String = ZigoEngine.doTween([clip1,clip2,clip3],"_scale",100,null,0,{skipLevel:1});
	* // results: "_xscale,_yscale||_xscale,_yscale"
	* // because clip2's scale was already set to 100 and the skipLevel is set to skip no-change tweens.</pre>
	*/
	public static function doTween(targets:Object, props:Object, endvals:Object, seconds:Number, ease:Object, delay:Number, callback:Object):String  
	{
		if (extensions.fuse.addBuildItem(arguments)==true) return null; // used by Fuse Simple Syntax
		// initialize + setup (ZManager singleton required prior to parse)
		if (instance==undefined || (Object(tweenHolder).proof==undefined && updateTime==undefined)) {
			if (MovieClip.prototype.tween!=null && typeof _global.$tweenManager=='object') {
				FuseKitCommon.error('003'); // ignores OUTPUT_LEVEL since this is crucial
			}
			instance = new ZManager();
			_playing = false;
		}
		// targets & properties - make use of ZManager's parser which converts both to arrays.
		var params:Object = instance.paramsObj(targets, props, endvals);
		//var pa:Array = ((params.pa.length==0) ? undefined : params.pa);
		var ta:Array = ((params.tg[0]==null || params.tg.length==0) ? undefined : params.tg);
		if (params.pa==undefined || ta==undefined || arguments.length<3) {
			if (extensions.fuseItem!=null && typeof ta[0]=='object') {
				return extensions.fuseItem.doTween(arguments[0]); // Route to FuseItem if Obj Syntax
			}
			if (OUTPUT_LEVEL>0) {
				if (arguments.length<3) FuseKitCommon.error('004',String(arguments.length));
				else FuseKitCommon.error('005',ta.toString(),(params.pa).toString());
			}
			return null;
		}
		if (_playing!=true) {
			setup();
		}
		
		// duration
		if (seconds==null || _global.isNaN(seconds)==true) seconds = (DURATION || 1);
		else if (seconds<0.01) seconds = 0;
		
		// delay
		if (delay<0.01 || delay==null || _global.isNaN(delay)==true) delay = 0;
		
		// callbacks (leave before easing)
		var validCBs:Object = (parseCallback(callback,ta)); // throws its own errors, returns undefined if not valid.
		
		// easing
		var eqf:Function;
		if (typeof ease=='function') {
			if (typeof (Function(ease).call(null,1,1,1,1))=='number') eqf = Function(ease);
			else if (OUTPUT_LEVEL>0) FuseKitCommon.error('014',ease);
		}
		else if (ease==null || ease=='') {
			if (EASING instanceof Function) eqf = Function(EASING);
			else if (extensions.pennerEasing!=undefined) ease = EASING;
		}
		if (typeof ease=='string' && ease!='') {
			if (extensions.pennerEasing[ease]!=undefined) eqf = extensions.pennerEasing[ease];
			else if (OUTPUT_LEVEL>0) FuseKitCommon.error('006',ease);
		}
		else if (typeof ease=='object' && ease.ease!=null && ease.pts!=null) {// object from custom easing tool
			eqf = Function(ease.ease);
			validCBs.extra1 = ease.pts;
		}
		if (typeof eqf!='function'){ // fallback default: easeOutQuint
			eqf = (function (t:Number, b:Number, c:Number, d:Number):Number { return c*((t=t/d-1)*t*t*t*t + 1) + b; });
		}
		// init each target & add tweens
		var propsAdded:String = '';
		for (var i:String in ta) {
			var o:Object = ta[i];
			if (o.__zigoID__==null) {
				initializeTargets(o); // initializes AsBroadcaster and adds __zigoID__
			}
			else if (instance.getStatus('locked',o)==true) {
				if (OUTPUT_LEVEL>0) FuseKitCommon.error('007',((o._name!=undefined)?o._name:(o.toString())),(params.pa).toString());
				continue;
			}
			//ZManager.addTween(obj:Object, props:Array, endvals:Array, seconds:Number, ease:Function, delay:Number, callback:Object):String
			var pStr:String = (instance.addTween(o, params.pa, params.va, seconds, eqf, delay, validCBs));
			propsAdded = (((pStr==null)?'|':pStr+'|')+propsAdded);
		}
		propsAdded = propsAdded.slice(0,-1);
		return ((propsAdded=='' || propsAdded=='|') ? null : propsAdded);
	}
	
	/**
	* Remove specific or all tweening properties from specific or all tweening targets in engine.
	* @param targs		a single target object, array of targets, or  for every active target
	* @param props		a property string, array of property strings, null or nothing for all props, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	*/
	public static function removeTween (targs:Object, props:Object):Void {
		instance.removeTween(targs, props);
	}
	
	/**
	* Test if a target and optionally a specific property is being handled by the engine.
	* @param targ		a single target object to test
	* @param props		a property string, null or nothing for any property, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	* @return			true if a matching active tween is found, which may be paused or playing 
	*/
	public static function isTweening(targ:Object, prop:String):Boolean {
		return Boolean(instance.getStatus('active',targ,prop));
	}
	
	/**
	* Returns the number of tweens active in a target object.
	* @param targ		target tween passed to determine if active, or {@link com.mosesSupposes.fuse.FuseKitCommon#ALL} for every active target
	* @return			number of active tweens, which may be paused or playing
	*/
	public static function getTweens(targ:Object):Number {
		return Number(instance.getStatus('count',targ));
	}
	
	/**
	* Locks a target to prevent tweens from running until target is unlocked.
	* @param targ		Object to lock
	* @param setLocked	locked value
	*/
	public static function lockTween(targ:Object, setLocked:Boolean):Void {
		instance.alterTweens('lock',targ,setLocked);
	}
	
	/**
	* Locks tweens and prevents from running until tween is unlocked.
	* @param targ		Object to lock
	* @return 			locked value
	*/
	public static function isTweenLocked(targ:Object):Boolean {
		return Boolean(instance.getStatus('locked',targ));
	}
	
	/**
	* Fast-forwarding a tween ends it and removes it from the engine, and triggers the onTweenEnd broadcast and end callbacks. 
	* @param targs		a single target object, array of targets, or {@link com.mosesSupposes.fuse.FuseKitCommon#ALL} for every active target
	* @param props		a property string, array of property strings, null or nothing for all props, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	*/
	public static function ffTween(targs:Object, props:Object):Void {
		instance.alterTweens('ff',targs,props);
	}
	
	/**
	* Rewinds and either pauses or restarts one or more tweens
	* @param targs		a single target object, array of targets, or {@link com.mosesSupposes.fuse.FuseKitCommon#ALL} for every active target
	* @param props		a property string, array of property strings, null or nothing for all props, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	* @param pauseFlag	true to rewind-and-pause
	* @param suppressStartEvents	if true is not passed, engine will refire 'onTweenStart' event and any start callbacks associated with the tween
	*/
	public static function rewTween(targs:Object, props:Object, pauseFlag:Boolean, suppressStartEvents:Boolean):Void {
		instance.alterTweens('rewind',targs,props,pauseFlag,suppressStartEvents);
	}
	
	/**
	* Test whether any or a specific property is paused in a target object
	* @param targ		a single target object to test
	* @param props		a property string, null or nothing for any property, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	* @return			paused value
	*/
	public static function isTweenPaused(targ:Object, prop:String):Boolean {
		return Boolean(instance.getStatus('paused',targ,prop));
	}
	
	/**
	* Pause one or more tweens
	* @param targs		a single target object, array of targets, or {@link com.mosesSupposes.fuse.FuseKitCommon#ALL} for every active target
	* @param props		a property string, array of property strings, null or nothing for all props, {@link com.mosesSupposes.fuse.FuseKitCommon#ALLCOLOR} for any active color transform
	*/
	public static function pauseTween(targs:Object, props:Object):Void {
		instance.alterTweens('pause',targs,props);
	}
	
	/**
	* Legacy - see {@link #resumeTween}
	*/
	public static function unpauseTween(targs:Object, props:Object):Void {
		instance.alterTweens('unpause',targs,props);
	}
	
	/**
	* Unpause one or more tweens.
	*/
	public static function resumeTween(targs:Object, props:Object):Void {
		instance.alterTweens('unpause',targs,props);
	}
	
	
	// -------------------------------------------------------------------------------------
	// - Flash7 Color Helpers - You may find these useful any time, not just when tweening.
	// (thanks goes out to R. Penner for most of the color math here)
	// -------------------------------------------------------------------------------------
	
	/**
	 *  (General) Color-transforms a target by keyword.
	 *  @description	Example: <code>ZigoEngine.setColorByKey(my_mc, 'tint', 50, 0x33FF00);</code>
	 *  @param targetObj	MovieClip or target to alter
	 *  @param type			String <code>"brightness", "brightOffset", "contrast", "invertColor", or "tint"</code>
	 *  @param amt			Percentage, can be negative in some cases
	 *  @param rgb			For "tint", a color value in 0x000000 or "#000000" format
	 */
	public static function setColorByKey(targetObj:Object, type:String, amt:Number, rgb:Object):Void {
		(new Color(targetObj)).setTransform(getColorTransObj(type,amt,rgb));
	}
	
	/**
	 *  (General) Generates a generic Flash7-style color-transform object with props like ra, etc., by keyword.
	 *  @param type			String <code>"brightness", "brightOffset", "contrast", "invertColor", or "tint"</code>
	 *  @param amt			Percentage, can be negative in some cases
	 *  @param rgb			For "tint", a color value in 0x000000 or "#000000" format
	 */
	public static function getColorTransObj(type:String, amt:Number, rgb:Object):Object {
		switch (type) {
		 case 'brightness' : // amt:-100=black, 0=normal, 100=white
			var percent:Number = (100-Math.abs(amt));
			var offset:Number = ((amt>0) ? (255*(amt/100)) : 0);
			return {ra:percent, rb:offset, ga:percent, gb:offset, ba:percent, bb:offset};
		 case 'brightOffset' : // "burn" effect. amt:-100=black, 0=normal, 100=white
			return {ra:100, rb:(255*(amt/100)), ga:100, gb:(255*(amt/100)), ba:100, bb:(255*(amt/100))};
		 case 'contrast' : // amt:0=gray, 100=normal, 200=high-contrast, higher=posterized.
			return {ra:amt, rb:(128-(128/100*amt)), ga:amt, gb:(128-(128/100*amt)), ba:amt, bb:(128-(128/100*amt))};
		 case 'invertColor' : // amt:0=normal,50=gray,100=photo-negative
			return {ra:(100-2*amt), rb:(amt*(255/100)), ga:(100-2*amt), gb:(amt*(255/100)), ba:(100-2*amt), bb:(amt*(255/100))};
		 case 'tint' : // amt:0=none,100=solid color (>100=posterized to tint, <0=inverted posterize to tint)
		 	if (rgb!=null) {
		 		var rgbnum:Number;
				if (typeof rgb=='string') {// Hex strings are not interpreted as relative values like other color props. Allow strings like "#FFFFFF" or "FFFFFF"
					if (rgb.charAt(0)=='#') rgb = rgb.slice(1);
					rgb = ((rgb.charAt(1)).toLowerCase()!='x') ? ('0x'+rgb) : (rgb);
				}
				rgbnum = Number(rgb);
				return {ra:(100-amt), rb:(rgbnum >> 16)*(amt/100), ga:(100-amt), gb:((rgbnum >> 8) & 0xFF)*(amt/100), ba:(100-amt), bb:(rgbnum & 0xFF)*(amt/100)};
			}
		}
		return {rb:0, ra:100, gb:0, ga:100, bb:0, ba:100}; // full reset
	};

	/**
	*  (General) Provides readings by keyword for a target object or Flash7-style color-transform object.
	*  @description	Inherent rounding errors are common! Especially when percentages are below 50. Even .tintString hex values may differ slightly.
	*  @param targOrTransObj	target object or Flash7-style color-transform object with props like ra, etc.
	*  @return					object may contain props brightness, brightOffset, contrast, invertColor, tint, tintPercent, and tintString
	*/
	public static function getColorKeysObj(targOrTransObj:Object):Object {
		var trans:Object = (targOrTransObj.ra!=undefined) ? targOrTransObj : (new Color(targOrTransObj)).getTransform();
		var o:Object = {};
		var sim_a:Boolean = (trans.ra==trans.ga && trans.ga==trans.ba);
		var sim_b:Boolean = (trans.rb==trans.gb && trans.gb==trans.bb);
		var tintPct:Number = (sim_a==true) ? (100 - trans.ra) : 0;
		if (tintPct!=0) {
			var ratio:Number = 100/tintPct;
			o.tint = ((trans.rb*ratio)<<16 | (trans.gb*ratio)<<8 | trans.bb*ratio);
			o.tintPercent = tintPct;
			var hexStr:String = o.tint.toString(16);
			var toFill:Number = 6 - hexStr.length;
			while (toFill-->0) hexStr = '0' + hexStr;
			o.tintString = '0x'+hexStr.toUpperCase();
		}
		if (sim_a==true && sim_b==true) {
			if (trans.ra<0) o.invertColor = (trans.rb * (100/255));
			else if (trans.ra==100 && trans.rb!=0) o.brightOffset =(trans.rb * (100/255));
			if (trans.ra!=100) {
				if ((trans.rb==0 || (trans.rb!=0 && (255*((100-trans.ra)/100))-trans.rb <= 1))) { // <=1:rounding error correct
					o.brightness = ((trans.rb!=0) ? (100-trans.ra) : (trans.ra-100));
				}
				if ((128-(128/100*trans.ra))-trans.rb <= 1) o.contrast = trans.ra; // <=1:rounding error correct
			}
		}
		return o;
	}
	
	/**
	 * @exclude
	 * Internal method that prepares any number of targets with AsBroadcaster functionality and a hidden engine ID.
	 */
	public static function initializeTargets():Void
	{
		for (var i:String in arguments) {
			var obj:Object = arguments[i];
			// this catches mc/btn/tf prototypes, which should not be directly initialized. However other class prototypes will not be caught and may result in recursion errors.
			// Please let me know if there is a way (f7 compatible) to detect whether an object is a prototype or not!
			if (obj==MovieClip.prototype || obj==Button.prototype || obj==TextField.prototype || obj==Object.prototype) {
				if (obj.oldAddListener==undefined) {
					if (obj==TextField.prototype) {
						obj.oldAddListener = obj.addListener; // TextField class is already initialized, this prevents recursion errors
						_global.ASSetPropFlags(obj, 'oldAddListener', 7, 1);
					}
					obj.addListener = function(o:Object):Void { // (this bit is only used if simpleSetup is called and addListener is called on a target before tweening it.)
						if ((this).__zigoID__==undefined) ZigoEngine.initializeTargets((this));
						if ((this) instanceof TextField) {
							Function((this).oldAddListener).call((this),o);// workaround: flash mc & tf behavior differs
						}
						else (this).addListener(o);
					};
					if (obj==MovieClip.prototype) { // hides the fake-in method above from for-in loops and deletion.
						_global.ASSetPropFlags(obj, 'addListener', 7, 1);
					}
				}
			}
			else if (obj.__zigoID__==undefined) {
				obj.__zigoID__ = zigoIDs;/*	although this is intrusive (in the same way AsBroadcaster or EventDispatcher is),
					  it's faster & safer than storing a reference list which can impair garbage collection and lead to bloat. */
				_global.ASSetPropFlags(obj, '__zigoID__', 7, 1);
				zigoIDs++;
				if (obj._listeners==null || obj.addListener==null) {
					AsBroadcaster.initialize(obj); // instances
				}
			}
		}
	}
	
	/**
	 * @exclude
	 * Internal method that strips AsBroadcaster functionality and hidden engine ID from any target.
	 */
	public static function deinitializeTargets():Void
	{
		for (var i:String in arguments) { 
			var obj:Object = arguments[i];
			if (obj.__zigoID__!=undefined) {
				_global.ASSetPropFlags(obj, '__zigoID__,_listeners,broadcastMessage,addListener,removeListener', 0, 2); // 0,2 is NOT a mistake, do not change
				delete obj.__zigoID__;
				delete obj._listeners;
				delete obj.broadcastMessage;
				delete obj.addListener;
				delete obj.removeListener;
			}
			if (obj.oldAddListener!=undefined) { // reset textfield prototype to prior addListener method.
				_global.ASSetPropFlags(obj, 'oldAddListener', 0, 2);
				obj.addListener = obj.oldAddListener;
				delete obj.oldAddListener;
			}
		}
	}
	
	/**
	 * @exclude
	 * Internal relay for use by ZManager instance only
	 * @param inst			ZManager instance
	 * @param deinitFlag	true stops engine
	 */ 
	public static function __mgrRelay(inst:ZManager,method:String,args:Array):Void
	{
		if (inst==instance) {
			Function(ZigoEngine[method]).apply(ZigoEngine,args);
		}
	}
	
	// ---------------------------------------------------------------------------------
	// -- Private ----------------------------------------------------------------------
	// ---------------------------------------------------------------------------------

	/**
	* Internal method that initializes the engine to start or stop executing updates on a pulse
	* @param deinitFlag		true to stop engine
	*/
	private static function setup(deinitFlag:Boolean):Void 
	{
		// deinit
		if (deinitFlag==true) {
			_playing = false;
			clearInterval(updateIntId);		
			delete tweenHolder.onEnterFrame;
			return;
		}
		// init
		instance.cleanUp();
		clearInterval(updateIntId);
		delete updateIntId;
		if(updateTime!=null && updateTime>0) {
			updateIntId = setInterval(instance, 'update', updateTime);
		}
		else {
			if(Object(tweenHolder).proof==null) { // fixes bug with "Simulate Download" (tweenHolder is still movieclip, but not on the stage)
				setControllerDepth(6789); // creates tweenHolder
				Object(tweenHolder).proof = 1;
			}
			var _inst:ZManager = instance;
			tweenHolder.onEnterFrame = function() {
				_inst.update.call(_inst);
			};
		}
		_playing = true;
		instance.now = getTimer();
	}
	
	/**
	 * Internal parser for callback parameter which can be various formats.
	 * User may pass easyfunc-string if Shortcuts class is registered, loose function as function or string, or callback object.
	 * @param callback		Parameter as passed by user to doTween or doShortcut
	 * @param targets		Tween targets, used in locating functions
	 * @return				A formatted object containing properties skipLevel,cycles, and possibly extra1,extra2,start,upd,end
	 */
	private static function parseCallback(callback:Object, targets:Array):Object
	{
		var validCBs:Object = { skipLevel:SKIP_LEVEL, cycles:1 };
		if (callback.skipLevel!=undefined && typeof callback.skipLevel=='number' && callback.skipLevel!=SKIP_LEVEL) {
			if (callback.skipLevel>=0 && callback.skipLevel<=2) validCBs.skipLevel = callback.skipLevel;
		};
		if (callback.cycles!=undefined) {
			if (typeof callback.cycles=='number' && callback.cycles>-1) validCBs.cycles = callback.cycles;
			else if ((callback.cycles.toUpperCase())=='LOOP') validCBs.cycles = 0; // 0 is infinite loop flag.
		}
		if (callback.extra1!=undefined) validCBs.extra1 = callback.extra1;
		if (callback.extra2!=undefined) validCBs.extra2 = callback.extra2;
		if (callback==undefined) {
			return validCBs;
		}
		var cbErrors:Array = [];
		// String-type callback: enable strings like "_root.someFunc('stringarg',300,this.somevar);" -- Shortcuts must be registered first.
		var ezf:String;
		if (typeof callback=='string') ezf = String(callback);
		else if (typeof callback.easyfunc=='string') ezf = callback.easyfunc;
		if (ezf!=undefined && ezf.indexOf("(")>-1 && ezf.indexOf(")")>-1) {
			if (extensions.shortcuts!=undefined) callback = extensions.shortcuts.parseStringTypeCallback(ezf);
			else if (OUTPUT_LEVEL>0) FuseKitCommon.error('008');
		}
		else if (typeof callback=='function' || typeof callback=='string') {
			callback = {func:callback};
		}
		// now parse callback object for engine, or throw errors if not parseable.
		for (var i:String in callback) { // for-in enables detection of missing funcs that are declared in an object. a missing func passed in straight for callback is not detectable since it's undefined.
			var fi:Number = (i.toLowerCase()).indexOf('func');
			if (fi>-1) {
				var prefix:String = i.slice(0,fi);
				var func:Object = callback[i];
				var args:Object = callback[prefix+'args'];
				var scope:Object = callback[prefix+'scope'];
				// try to locate an unscoped or mis-scoped func-string - in any target/_parent,_root,_global,finally via eval.
				if (typeof func=='string' && scope[func]==undefined) { 
					for (var j:String in targets) {
						var targ:Object = targets[j];
						if (typeof targ[func]=='function') {
							scope = targ;
							break;
						}
						if (typeof targ._parent[func]=='function') {
							scope = targ._parent;
							break;
						}
					}
					if (scope==undefined && _level0[func]!=undefined) scope = _level0;
					if (scope==undefined && _global[func]!=undefined) scope = _global;
				}
				if (typeof func!='function') {
					if (typeof scope[String(func)]=='function') func = scope[String(func)];
					else func = eval(String(func));
				}
				if (func==undefined) {
					cbErrors.push(String(i+':'+((typeof callback[i]=='string')?'"'+callback[i]+'"':callback[i])+'/'+prefix+'scope:'+(scope)));
				}
				else {
					if (args!=undefined && !(args instanceof Array)) args = [args];
					if (prefix=='') prefix = 'end';
					validCBs[prefix] = {s:scope, f:func, a:args, id:cbTicker++};
					if (prefix=='start') validCBs.start.fired = false;
				}
			}
			else if ((FuseKitCommon._cbprops()).indexOf('|'+i+'|')==-1) {
				FuseKitCommon.error('009',i);
			}
		}
		if (cbErrors.length>0 && OUTPUT_LEVEL>0) {
			if (OUTPUT_LEVEL>0) FuseKitCommon.error('010',cbErrors.length,cbErrors.toString());
		}
		return validCBs;
	}
}